package coop.bancocredicoop.proyectos.gd

import org.apache.commons.logging.LogFactory
import grails.transaction.Transactional
import grails.gsp.PageRenderer
import org.codehaus.groovy.grails.web.mapping.LinkGenerator
import org.hibernate.criterion.CriteriaSpecification

import coop.bancocredicoop.proyectos.gd.alfresco.Utils

import coop.bancocredicoop.proyectos.gd.Box;
import coop.bancocredicoop.proyectos.gd.Customer;
import coop.bancocredicoop.proyectos.gd.Document;
import coop.bancocredicoop.proyectos.gd.DocumentType;

@Transactional
class DocumentService {

    static final log = LogFactory.getLog(this)
    def grailsApplication
	def crecerxxiWrapper
    PageRenderer groovyPageRenderer
    def mailService
    LinkGenerator grailsLinkGenerator

    def createDocument(customerId, cajaId, documentTypeId, expirationDate, tags, documentFile, loggedInUser, now, notes, realVersion = null) {

        def documentType = DocumentType.get(documentTypeId)
        def customer = crecerxxiWrapper.findOrCreateCustomer(customerId.toLong())
        def documentName = getDocumentName(customer, documentType)
        def box = getOrCreateBox(cajaId)
		def currentVersion = currentVersion(customer, documentType)
		
		if (!realVersion)
			realVersion = currentVersion && !currentVersion.deleteDate ? currentVersion.realVersion + 1 : 1

        def document = new Document(name: documentName, 
                              customer: customer,
                              box: box,
                              documentType: documentType,
                              validTo: expirationDate,
                              tags: tags,
                              lastVersion: true,
                              createdByUserName: loggedInUser,
                              createdDate: now,
                              notes: notes,
							  realVersion: realVersion)
       
        invalidatePreviousVersions(document) 
        
        // TODO: extract the GMT from environment        
        def validTo = expirationDate.format("yyyy-MM-dd'T'00:00:00-03:00")

        def client = new Utils(grailsApplication.config.alfresco)
                                .getClient(loggedInUser: loggedInUser)

        def result = client.uploadFile(customer: customerId, 
                                       document: documentFile,
                                       title: documentType.shortName,
                                       documentName: documentName, 
                                       validTo: validTo,
                                       tags: tags)
 
        if (result.success) {
            document.documentVersion = result.data.version.label
            document.save(failOnError: true)
            result.document = document
            sendUploadNotification(document)
        } else {
            throw new RuntimeException(result.message) 
        }

		if (documentFile.metaClass?.respondsTo(documentFile, "delete")) {
			def deleted = documentFile.delete()
        	if (!deleted) {
            	log.error "File could not be deleted: ${documentFile.getOriginalFilename()}"
        	}
		}

        result

    }

    private def getDocumentName(customer, documentType) {
        
        if (documentType.versionable) {
            "${documentType.shortName}.pdf"
        } else {
            def prevDocuments = prevDocumentQuery(customer, documentType).list()
            def nextDocumentNumber = prevDocuments.size() + 1
            String.format("${documentType.shortName}-%03d.pdf", nextDocumentNumber)
        }

    }

    private def invalidatePreviousVersions(document) {
        if (document.documentType.versionable) {
            def prevDocument = prevDocumentQuery(document.customer, document.documentType) 
            prevDocument.updateAll(lastVersion: false)
        }
    }
    
    private def prevDocumentQuery(customer, documentType) {

        Document.where {
            customer == customer 
            documentType == documentType 
            lastVersion == true
        }

    }

    def needsNewVersionConfirmation(customerId, documentTypeId) {

        def documentType = DocumentType.get(documentTypeId)
        if (!documentType.versionable) {
            log.info "El tipo de documento no es versionable: ${documentTypeId}"
            return false
        }
        
        def customer = Customer.findByIdPersona(customerId)
        if (!customer) {
            log.info "El cliente no existe: ${customerId}"
            return false
        }

        def prevDocument = prevDocumentQuery(customer, documentType).get()
        def previousExists = prevDocument != null
        if (previousExists) {
            log.info "Existe una version anterior del documento: ${customerId}, ${documentTypeId}, ${prevDocument}"
        }
        previousExists

    }

    def getOrCreateBox(cajaId_) {
        def box = Box.where { cajaId == cajaId_ }.find()
        if (box == null) {
            def newBox = new Box(cajaId: cajaId_)
            newBox.save()
            return newBox
        } else {
            return box
        }
        
    }
        
	def currentVersion(customer, documentType) {
		if (documentType.versionable) {
			Document.findByCustomerAndDocumentTypeAndLastVersion(customer, documentType, true)
		}
	}

	def anular(Document documentInstance, userName) {
		def now = new Date()
				
		documentInstance.deleteByUserName = userName
		documentInstance.deleteDate = now
		
		def previousLastVersion = Document.findByCustomerAndDocumentTypeAndRealVersionAndDeleteDate(
				documentInstance.customer, documentInstance.documentType,
				documentInstance.realVersion - 1, null)
		
		if (previousLastVersion) {
			def byteArrayContent = new Utils(grailsApplication.config.alfresco).
					getClient(loggedInUser: userName).
					getContentAsByteArray(previousLastVersion)

			def documentFile = [inputStream: new ByteArrayInputStream(byteArrayContent),
					originalFilename: 'x.pdf']

			def result = createDocument(
					previousLastVersion.customer.idPersona as String,
					previousLastVersion.box.cajaId,
					previousLastVersion.documentType.id,
					previousLastVersion.validTo,
					previousLastVersion.tags ?: '',
					documentFile,
					previousLastVersion.createdByUserName,
					previousLastVersion.createdDate,
					previousLastVersion.notes,
					previousLastVersion.realVersion)
			
			previousLastVersion.deleteByUserName = userName
			previousLastVersion.deleteDate = now
			
			result
		}
			

	}

    def sendUploadNotification(document) {
        
        def documentType = document.documentType
        if (!documentType.mustSendNotificationOnUpload()) {
            return
        }
        def model = [document: document,
                     docUrl: grailsLinkGenerator.link(
                                    controller: 'document', 
                                    action: 'openFileFromRepository', 
                                    id: document.id, 
                                    absolute: true)
                    ]
        log.info "Enviando mail a ${documentType.uploadNotificationEmailAddress}"            
        try {
            mailService.sendMail { 
                to documentType.uploadNotificationEmailAddress
                subject "Documento '${documentType.name}' cargado"
                html groovyPageRenderer.render(view: '/email/documentUploaded', model: model)
           }
        } catch (Exception e) { 
            log.fatal("Error enviando email", e)
        }
    }

    def getDocumentsByTypesAndDate(documentTypes, dateFrom, dateTo) {
        
        def criteria = Document.createCriteria()
        def results = criteria.list { 
                criteria.and {
                    isNull('deleteDate')
                    inList 'documentType', documentTypes 
                    eq 'lastVersion', true 
                    gt 'createdDate', dateFrom
                    le 'createdDate', dateTo
                }
                order('createdDate', 'asc')
        }
        results
 
    }

    private countByCriteria(criteriaBuilder, idPersona=null, cajaId=null, dateFrom=null, dateTo=null) {

        criteriaBuilder.and {
			
			isNull('deleteDate')
			
            if (idPersona) {
                customer { eq('idPersona', idPersona as Long) }
            }
        
            if (cajaId) {
                box { eq('cajaId', cajaId) }
            }
            if (dateFrom) {
                gt ('createdDate', dateFrom) 
            }

            if (dateTo) {
                le ('createdDate', dateTo) 
            }
        }
    }

    private countByProperty(propertyName, idPersona, cajaId, dateFrom, dateTo, offset, max) {

        def criteria = Document.createCriteria()
        def result = criteria.list (max: max, offset: offset) {
            // mandatory resultTransformer, see http://jira.grails.org/browse/GRAILS-9644
            resultTransformer(CriteriaSpecification.ALIAS_TO_ENTITY_MAP)
            projections {
                property(propertyName, propertyName)
                count(propertyName, "amount")
                groupProperty(propertyName)
            }
            countByCriteria(criteria, idPersona, cajaId, dateFrom, dateTo)
            order('amount', 'desc')
        }

        def countCriteria = Document.createCriteria()
        def totalCount = countCriteria.get {
            projections {
                countDistinct(propertyName)
            }
            countByCriteria(countCriteria)            
        }

        [items: result, totalItems: totalCount, offset: offset]
    }

    def countByCustomer(idPersona=null, cajaId=null, dateFrom=null, dateTo=null, offset=0, max=10) {
        countByProperty("customer", idPersona, cajaId, dateFrom, dateTo, offset, max) 
    }
    
    def countByBox(idPersona=null, cajaId=null, dateFrom=null, dateTo=null, offset=0, max=10) {
        countByProperty("box", idPersona, cajaId, dateFrom, dateTo, offset, max) 
    }

    def countByDate(idPersona=null, cajaId=null, dateFrom=null, dateTo=null, offset=0, max=10) {
        countByProperty("dayCreated", idPersona, cajaId, dateFrom, dateTo, offset, max) 
    }

	def versionsByFile(idPersona, cajaId, dateFrom, dateTo, offset, max) {
		
		def getConditions = { criteriaBuilder ->
		
			criteriaBuilder.and {

				isNull('deleteDate')
				
				if (idPersona) {
                    def customer = Customer.findByIdPersona(idPersona as Long) 
					eq('customer', customer)
				}
				
				if (cajaId) {
					box { eq('cajaId', cajaId) }
				}
				
				if (dateFrom) {
					gt ('createdDate', dateFrom)
				}
				
				if (dateTo) {
					le ('createdDate', dateTo)
				}
			}
		
		}
		
		def criteria = Document.createCriteria()
		
		def result = criteria.list (max: max, offset: offset) {
			createAlias "customer", "customer"
			createAlias "documentType", "documentType"
			getConditions(criteria)
			order("customer.idPersona",  'asc')
			order("documentType.name",  'asc')
		}
		
		def totalCount = result.totalCount
		
		[items: result, totalItems: totalCount, offset: offset]
	}
		
	
}	
